﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;

namespace nwrole.plugin.util
{
    public static class EventLogger
    {
        private static string DefaultOrigin = @"General";
        private static Dictionary<string, EventLog> EventLogs = new Dictionary<string, EventLog>(StringComparer.OrdinalIgnoreCase);

        //private static string LogName = "nwrole";

        //private static EventLog GetEventLog (string source)
        //{
        //    if (!EventLogs.ContainsKey(source))
        //    {
        //      // TODO Check if access the uncomment
        //        //if (!EventLog.SourceExists(source))
        //        //    EventLog.CreateEventSource(source, LogName);
        //        //EventLog eventLog = new EventLog();
        //        //eventLog.Log = LogName;
        //        //eventLog.Source = source;
        //        //eventLog.MaximumKilobytes = 128 * 1024;
        //        //EventLogs.Add(source, eventLog);
        //    }
        //    return EventLogs[source];
        //}

        public static void LogInformation (string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, message, EventLogEntryType.Information);
        }

        public static void LogInformation (string origin, string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), origin, message, EventLogEntryType.Information);
        }

        public static void LogWarning (string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, message, EventLogEntryType.Warning);
        }

        public static void LogWarning (string origin, string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), origin, message, EventLogEntryType.Warning);
        }

        public static void LogError (string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, message, EventLogEntryType.Error);
        }

        public static void LogError (string origin, string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), origin, message, EventLogEntryType.Error);
        }

        public static void LogFailureAudit (string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, message, EventLogEntryType.FailureAudit);
        }

        public static void LogFailureAudit (string origin, string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), origin, message, EventLogEntryType.FailureAudit);
        }

        public static void LogSuccessAudit (string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, message, EventLogEntryType.SuccessAudit);
        }

        public static void LogSuccessAudit (string origin, string message)
        {
            Log(Assembly.GetCallingAssembly().ToString(), origin, message, EventLogEntryType.SuccessAudit);
        }

        public static void LogException (Exception exception)
        {
            LogException(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, exception, EventLogEntryType.Error);
        }

        public static void LogException (string origin, Exception exception)
        {
            LogException(Assembly.GetCallingAssembly().ToString(), origin, exception, EventLogEntryType.Error);
        }

        public static void LogException (Exception exception, EventLogEntryType entryType)
        {
            LogException(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, exception, entryType);
        }

        public static void LogException (string origin, Exception exception, EventLogEntryType entryType)
        {
            LogException(Assembly.GetCallingAssembly().ToString(), origin, exception, entryType);
        }

        public static void LogException (string source, string origin, Exception exception, EventLogEntryType entryType)
        {
            StringBuilder error = new StringBuilder();

            error.AppendLine(@"===========================================");
            error.AppendLine();
            error.Append("Date:".PadRight(20)).Append(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).AppendLine();
            //error.Append("Computer name:".PadRight(20)).Append(SystemInformation.ComputerName).AppendLine();
            //error.Append("Folder:".PadRight(20)).Append(Application.ExecutablePath).AppendLine();
            //error.Append("User name:".PadRight(20)).Append(SystemInformation.UserName).AppendLine();

            Process[] processes = Process.GetProcessesByName("System");
            if (System.Environment.OSVersion.Version.Major < 6)
            {
                if (processes.Length > 0)
                {
                    error.Append("System up-time:".PadRight(20)).Append((DateTime.Now - processes[0].StartTime).ToString());
                    error.AppendLine();
                }
            }

            error.Append("App up-time:".PadRight(20)).Append((DateTime.Now - Process.GetCurrentProcess().StartTime).ToString()).AppendLine();
            error.AppendLine();

            error.Append("Exception class:".PadRight(20)).Append(exception.GetType().ToString()).AppendLine();
            error.AppendLine();

            error.Append(GetExceptionStack(exception, 0)).AppendLine();
            error.AppendLine();

            error.AppendLine(@"===========================================");
            error.AppendLine();

            Log(source, origin, error.ToString(), entryType);
        }

        public static void Log (string message, EventLogEntryType entryType)
        {
            Log(Assembly.GetCallingAssembly().ToString(), DefaultOrigin, message, entryType);
        }

        public static void Log (string origin, string message, EventLogEntryType entryType)
        {
            Log(Assembly.GetCallingAssembly().ToString(), origin, message, entryType);
        }

        public static void Log (string source, string origin, string message, EventLogEntryType entryType)
        {
            /*StringBuilder msg = new StringBuilder();
            msg.Append("Origin:\t\t").Append(origin).AppendLine().AppendLine();
            msg.AppendLine(message);
            GetEventLog(source).WriteEntry(msg.ToString(), entryType);*/
        }

        private static string GetExceptionStack (Exception exception, int indent)
        {
            StringBuilder stack = new StringBuilder();
            StackTrace trace = new StackTrace(exception, true);

            for (int i = (trace.FrameCount - 1); i > 0; i--)
            {
                StackFrame frame = trace.GetFrame(i);
                if (frame == null)
                    continue;
                MethodBase method = frame.GetMethod();
                if (method == null)
                    continue;

                string fname = frame.GetFileName();
                stack.Append("".PadLeft(indent * 4)).Append("File:".PadRight(20)).Append(fname).AppendLine();
                stack.Append("".PadLeft(indent * 4)).Append("Method:".PadRight(20)).Append(method.Name).AppendLine();
                stack.Append("".PadLeft(indent * 4)).Append("Line#:".PadRight(20)).Append(frame.GetFileLineNumber().ToString()).AppendLine();

                foreach (ParameterInfo info in method.GetParameters())
                {
                    stack.Append("".PadLeft(indent * 4)).Append("  Parameter name:".PadRight(20)).Append(info.Name).AppendLine();
                    stack.Append("".PadLeft(indent * 4)).Append("  Parameter type:".PadRight(20)).Append(info.ParameterType).AppendLine();
                    stack.Append("".PadLeft(indent * 4)).Append("  Parameter pos:".PadRight(20)).Append(info.Position).AppendLine();
                }
            }

            stack.AppendLine().Append("".PadLeft(indent * 4)).Append("Target:".PadRight(20)).Append(exception.TargetSite).AppendLine();
            stack.Append("".PadLeft(indent * 4)).Append("Msg:".PadRight(20)).Append(exception.Message.Replace("\r\n", "\r\n" + "".PadLeft(indent * 4) + "".PadRight(20))).AppendLine();
            stack.Append("".PadLeft(indent * 4)).Append("Stack:".PadRight(20)).AppendLine();
            if(exception.StackTrace != null)
                stack.Append(exception.StackTrace.ToString().Replace("\r\n", "\r\n" + "".PadLeft(indent * 4) + "".PadRight(20)));
            stack.AppendLine();

            if (exception.InnerException != null)
            {
                stack.Append("Inner exception:").AppendLine();
                stack.Append(GetExceptionStack(exception.InnerException, indent + 1)).AppendLine();
            }

            stack.AppendLine();

            return stack.ToString();
        }
    }
}
